﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using Shoozla.DataModel;
using System.Drawing;
using System.Diagnostics;
using System.Windows;
using Shoozla.DataModel.Helper;
using MahApps.Metro.Controls;
using Shoozla.ViewModel;
using System.Collections.ObjectModel;
using System.Collections;
using Shoozla.View;
using System.Windows.Data;
using System.Threading.Tasks;

namespace Shoozla
{
    public class AppModel
    {

        #region Variables

        public static object Mp3ListSyncObj = new object(); //to lock the collection for modify or enumeration

        public static List<DirectoryInfo> FoldersRead; //cache
        public static ICoverStorage CoverImageCache;
        public static CoverSearcher CoverSearcher;
        public static List<CoverSearcher> AvailableCoverSearchers;
        public static IMP3Processor Mp3Processor;
        public static Mp3File CurrentEditingSong;  
        private static ObservableCollection<Mp3File> _totalSongs;
        public static ThreadingFactorEnum ThreadingFactor = ThreadingFactorEnum.Maximum; //num of threads      
        public static CancellationTokenSource MainCancellationTokenSource;
        public static ObservableCollection<IProgressOperation> ProgressOperationQueue { get; set; }        

        #endregion

        #region Mp3 stuff

        public static ObservableCollection<Mp3File> Songs
        {
            get
            {
                return _totalSongs;
            }
            set
            {
                if (value != null)
                {
                    _totalSongs = value;                 
                }
            }
        }

        /// <summary>
        /// return true if there is at least one song dirty that need to be saved
        /// </summary>
        /// <returns></returns>
        public static bool IsCollectionDirty()
        {
            if (AppModel.Songs != null)
                return AppModel.Songs.Any<Mp3File>((m) => m.Dirty);
            else
                return false;
        }

        //NOTE Song collection manipulation (ADD)
        public static void AddSongsToCollection(List<Mp3File> newSongs)
        {
            //need to do it in the UI thread because the CollectionView
            App.FolderView.Dispatcher.Invoke((Action)(() =>
            {
                lock (Mp3ListSyncObj)
                {
                    for (int i = 0; i < newSongs.Count; i++)
                        if (newSongs[i] != null)
                            AppModel.Songs.Add(newSongs[i]);

                    App.StatisticLogger.TotalFiles = AppModel.Songs.Count;
                }

                //update the list

                ((FolderListViewModel)App.FolderView.DataContext).DisplayedSongs = new ListCollectionView(AppModel.Songs);
            })
            );
        }

        ////NOTE Song collection manipulation (REMOVE)
        //public static void RemoveFolderFromSongsCollection(DirectoryInfo folder)
        //{
        //    //need to do it in the UI thread because the CollectionView
        //   App.FolderView.Dispatcher.Invoke((Action)(() =>
        //   {
        //       lock (Mp3ListSyncObj)
        //       {
        //           var match = (
        //                       from m in AppModel.Songs
        //                       where m.FileName.DirectoryName == folder.FullName
        //                       select m
        //                   ).ToList<Mp3File>();

        //           int num = match.Count();
        //           for (int i = 0; i < num; i++)
        //           {
        //               AppModel.Songs.Remove(match[i]);
        //           }

        //           App.StatisticLogger.TotalFiles = AppModel.Songs.Count;
        //           AppModel.FoldersRead.Remove(folder);
        //       }


        //       ((FolderListViewModel)App.FolderView.DataContext).DisplayedSongs = new ListCollectionView(AppModel.Songs);
        //   })
        //    );
        //}

        //Mp3 file read (total)
        //public static event EventHandler SongListChanged; 
       

        //static void _totalSongs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        //{
        //    if (SongListChanged != null)
        //        foreach (Delegate d in SongListChanged.GetInvocationList())
        //        {
        //            d.DynamicInvoke(new object[] { null, null });
        //        }
        //}
                
        public static List<Mp3File> SelectedSongs
        {
            get
            {
                if (AppModel.Songs == null)
                    return new List<Mp3File>();

                if (!AppModel.IsAppWorking)
                {
                    //get all selected items (binding on IsSelected property)
                    var match = (
                       from m in AppModel.Songs
                       where m.IsSelected == true
                       select m).ToList<Mp3File>();

                    return match;
                }
                else
                    return new List<Mp3File>();
            }

        }

        private static List<Mp3File> _notFoundMp3;
        /// <summary>
        /// Track the list of Mp3 for which we tried to find a cover and we failed
        /// </summary>
        public static List<Mp3File> NotFoundMp3
        {
            get
            {
                if (_notFoundMp3 == null)
                    return new List<Mp3File>();
                    return _notFoundMp3;
            }

            set { _notFoundMp3 = value;}

        }

        public static void SaveCollection(Window caller)
        {

            var modifiedSongs = (
                       from m in AppModel.Songs
                       where m.Dirty
                       select m
                   );

            FileSaver mp3FileSaver = new FileSaver(AppModel.MainCancellationTokenSource, AppModel.Mp3Processor);
            mp3FileSaver.Completed += delegate(object sender, ProgressOperationEventArgs e)
            {
                App.MainView.Dispatcher.BeginInvoke((Action)(() =>
                {
                    //save operation ended  
                    if (AppModel.ProgressOperationQueue.Contains(mp3FileSaver))
                        AppModel.ProgressOperationQueue.Remove(mp3FileSaver);

                    if(e.Current > 0)
                        MetroMessageBox.MetroShow("", "Saved " + e.Current + " files", true, caller, MessageIcon.Info, false);

                    if(e.Current < e.Maximum) //not all files saved (error/exception occured in the parrallel tasks)
                        MetroMessageBox.MetroShow("", "Oops! Not all files have been saved, the files are now highlighted, try to save them again", true, caller, MessageIcon.Error, false);
                }
                ));
            };

            if (mp3FileSaver.SaveCollection(modifiedSongs.ToList<Mp3File>()) > 5)
            {
                //display UI
                AppModel.ProgressOperationQueue.Add(mp3FileSaver);
                if (AppModel.ProgressOperationQueue.Count > 0) //only if we have at least 1 bigfolder
                    AppModel.StartProgressBarInternal(AppModel.ProgressOperationQueue.ToList<IProgressOperation>());
            }
            else
                mp3FileSaver.Start();

        }

        public static void ResetApplication()
        {
            StopAllWork();          
            Songs.Clear();
            NotFoundMp3.Clear();
            FoldersRead.Clear();
            App.StatisticLogger.Reset();

            //not deleting the image cache 
        }

        #endregion

     
        public static void StopAllWork()
        {
            //send the stop commands and wait to reset it
            AppModel.MainCancellationTokenSource.Cancel();
            AppModel.MainCancellationTokenSource.Token.WaitHandle.WaitOne();
            AppModel.MainCancellationTokenSource = new CancellationTokenSource();

            foreach(CoverSearcher cs in AppModel.AvailableCoverSearchers)
                cs.CancelTokenSource = AppModel.MainCancellationTokenSource;

            AppModel.CoverImageCache.CancelTokenSource = AppModel.MainCancellationTokenSource;

            AppModel.ProgressOperationQueue.Clear();
        }
      

        #region event callbacks

        public static void ProgressOperationQueue_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
          

            ////called when the progress bar has something to do or to not to do

            ////if (App.MainProgressBarView == null)
            ////{
            ////    //Thread newWindowThread = new Thread(new ParameterizedThreadStart(StartProgressBarInternal));
            ////    //newWindowThread.SetApartmentState(ApartmentState.STA);
            ////    //newWindowThread.IsBackground = true;
            ////    //newWindowThread.Start(new object());
                
            ////}
            ////else
            ////{
            //    if (e.NewItems == null) return;

            //    foreach (IProgressOperation op in e.NewItems)
            //    {
            //        //op.Started += delegate(object o,ProgressOperationEventArgs eva) {
            //        //    App.MainView.Dispatcher.BeginInvoke((Action)(()=>{
            //        //        StartProgressBarInternal(null);
            //        //        ((ProgressBarViewModel)App.MainProgressBarView.DataContext).DisplayProgressBar(op);
            //        //        //if(AppModel.ProgressOperationQueue.Count > 0)
            //        //        //    App.MainProgressBarView.ShowDialog(); //multithread issue, when the work is too fast, the list cna be empty
            //        //    }));
            //        //};

            //    }               

            //}


        }

        /// <summary>
        /// Execute the Progress Operations, displaying a progressbar and return (synchronous)
        /// </summary>
        /// <param name="op">List of operations to execute</param>
        public static void StartProgressBarInternal(List<IProgressOperation> op)
        {            
                ProgressBarView mainProgressBarView = new View.ProgressBarView();
                ((ProgressBarViewModel)mainProgressBarView.DataContext).AddWork(op);
                mainProgressBarView.ShowDialog();
        }     

        public static bool CoverSearcher_NotifyUser(string message, bool canCancel)
        {
          var res= MetroMessageBox.MetroShow(String.Empty, message, true, App.MainView,MessageIcon.None,canCancel);
          if (res == MessageBoxResult.OK)
              return true;
          else
              return false;
        }

        public static void CoverSearcher_SearchStarted(object sender, ProgressOperationEventArgs e)
        {
          
        }
       
        public static void CoverSearcher_SearchCompleted(object sender, ProgressOperationEventArgs e)
        {
           
            if (AppModel.ProgressOperationQueue.Contains(AppModel.CoverSearcher))
                AppModel.ProgressOperationQueue.Remove(AppModel.CoverSearcher); 

        }

        /// <summary>
        /// Iterates throught the songs list and apply the cover at all selected Mp3 that have the ImageUrl property set (if this URI has been found and saved in into the cache)
        /// </summary>
        public static void ApplyFoundCoversToSongs()
        {
            //go for all selected item and check if we have found the cover, if yes apply the cover
            //get all selected items (binding on IsSelected property)
            var selected = (
                from m in AppModel.Songs
                where m.IsSelected == true
                select m);

            foreach (Mp3File m in selected)
            {
                if (m.ImageUrl != null)
                {
                    if (AppModel.CoverImageCache.ContainsCover(m.ImageUrl.AbsoluteUri))
                    {                        
                        InternalThumbObject coverObject = AppModel.CoverImageCache[m.ImageUrl.AbsoluteUri];
                        if (coverObject != null)
                        {
                            m.Cover = coverObject.Cover;
                            m.IsSelected = false;
                        }
                    }
                }
            }
        }

        #endregion

        public static void ShowCoverPreviewWindow()
        {        
            ResultView coverView = new ResultView();
            coverView.ShowDialog();
            //saving collection after the close
           // AppModel.SaveCollection(App.MainView);
        }

        public static bool IsAppWorking
        {
            get
            {
                if (ProgressOperationQueue != null && ProgressOperationQueue.Count > 0)
                    return true;
                else
                    return false;
            }
        }

        #region Logging

        public static void LogEntry(string message)
        {
            Trace.WriteLine(message);
        }

        public static void LogEvent(string message, bool isError, int ID)
        {
            if (App.EventSourceCreated)
                EventLog.WriteEntry(App.EventSourceName, message, isError ? EventLogEntryType.Error : EventLogEntryType.Information, ID);
        }
        
        #endregion
    }
}
